package bank;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import mware_access.MiddlewareService;
import mware_access.NameService;
import bank_access.KontoException;
import bank_access.KontoImplBase;
import bank_to_bank.NoSuchAccountException;
import bank_to_bank.TransferHandlerImplBase;

/**
 * Klasse fuer ein Konto
 */
public class Account extends KontoImplBase {

//	private TransferHandler xferHandler; // the transferhandler to use for bank transfers
    private String ID; 			// Konto-ID
    private double KontoStand;  // Kontostand
    private String blz; 		// BLZ
    private String mware_host;
    private int mware_port;
    private Semaphore semaphore;

    public Account(String id, String BLZ) {
        // Initialisieren
        ID = id;
        KontoStand = 0.0;
        this.blz = BLZ;
        this.semaphore = new Semaphore(1, true);
    }

    public void setConnectionData(String host, int port) {
        this.mware_host = host;
        this.mware_port = port;
    }

    /**
     * Liefert die ID dieses Kontos.
     * (Wird fuer die GUI benutzt.)
     *
     * @return Konto-ID
     */
    protected String getID() {
        return ID;
    }

    /**
     * Returns the BLZ of the account's bank
     * @return the blz
     */
    public String getBLZ() {
        return blz;
    }

    /**
     * Abheben von dem Konto
     * @param betrag der abghehoben werden soll
     * @return der neue Kontostand
     *
     * @see bank_access.KontoImplBase#abheben(double)
     */
    @Override
    public double abheben(double betrag) throws KontoException {
    	try {
			if(semaphore.tryAcquire(6, TimeUnit.SECONDS)){
				Thread.sleep(8000);
				if (Double.MIN_VALUE + betrag > KontoStand) {
	                throw new KontoException("Konto kann nicht weiter ueberzogen werden.");
	            } else {
	                KontoStand = KontoStand - betrag;
	            }
				semaphore.release();
	            return KontoStand;
			}
		} catch (InterruptedException e) {
			e.printStackTrace();			
		}
		return Double.NaN;
    }

    /**
     * Geld auf das Konto einzahlen
     * @param der betrag der eingezahlt werden soll
     *
     * @see bank_access.KontoImplBase#einzahlen(double)
     */
    @Override
    public double einzahlen(double betrag) throws KontoException {
    	try {
			if(semaphore.tryAcquire(6, TimeUnit.SECONDS)){
	            if (Double.MAX_VALUE - betrag < KontoStand) {
	                throw new KontoException("Zu viel Geld auf dem Konto.");
	            } else {
	                KontoStand = KontoStand + betrag;
	            }
	            semaphore.release();
	            return KontoStand;	         
			}
    	} catch (InterruptedException e) {
			e.printStackTrace();			
		}
    	return Double.NaN;
    }

    /**
     * der Kontostand
     * @return der Kontostand
     * @see bank_access.KontoImplBase#kontoStand()
     */
    @Override
    public double kontoStand() {
        return KontoStand;
    }

    /**
     * Starts an atomic bank transfer operation via the TransferHandler defined in
     * the bank_to_bank package
     * @param betrag
     * @param bankleitzahl
     * @param empfaengerKontoID
     *
     * @see bank_access.KontoImplBase#ueberweise(double, java.lang.String, java.lang.String)
     */
    @Override
    public double ueberweise(double betrag, String bankleitzahl, String empfaengerKontoID)
            throws KontoException {

        // rebind xferHandler from NameService
        MiddlewareService mware = MiddlewareService.init(mware_host, mware_port);
        NameService nameservice = mware.getNameService();
        TransferHandlerImplBase xferHandler = (TransferHandlerImplBase) nameservice.lookup(bankleitzahl);

        // block account to make transfer an atomic operation
        // loop until acquisition was successful
        boolean acquired = false;
        try {
        	while(!acquired){
        		acquired = semaphore.tryAcquire(6, TimeUnit.SECONDS);
				// check if withdrawal is possible
	            if (!(Double.MIN_VALUE + betrag > KontoStand)) {
                    Boolean erfolg = xferHandler.gutschreiben(empfaengerKontoID, betrag);
                    if (erfolg) {
                        System.out.println("Account: gutschreiben -> true");
                        // if payment was successful, then do withdrawal on own account
        				Thread.sleep(8000);
        				if (Double.MIN_VALUE + betrag > KontoStand) {
        	                throw new KontoException("Konto kann nicht weiter ueberzogen werden.");
        	            } else {
        	                KontoStand = KontoStand - betrag;
        	            }
                    } else {
                    	System.out.println("Account: gutschreiben -> false");
                    }                 
                } 
	            semaphore.release();                
        	}
        } catch (InterruptedException e) {
			e.printStackTrace();			
		} catch (NoSuchAccountException e) {
            throw new KontoException("Das Empfaengerkonto existiert nicht.");
        }
        return this.kontoStand();
    }
}
